// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package net

import (
	"context"
	"internal/itoa"
	"io"
	"os"
	"syscall"
	"time"
)

// BUG（mikio）：在JS和Windows上，没有实现TCPConn和
// TCPListener的文件方法。

// TCPAddr表示TCP端点的地址。
type TCPAddr struct {
	IP   IP
	Port int
	Zone string // IPv6作用域寻址区域
}

// 网络返回地址的网络名称“tcp”。
func (a *TCPAddr) Network() string { return "tcp" }

func (a *TCPAddr) String() string {
	if a == nil {
		return "<nil>"
	}
	ip := ipEmptyString(a.IP)
	if a.Zone != "" {
		return JoinHostPort(ip+"%"+a.Zone, itoa.Itoa(a.Port))
	}
	return JoinHostPort(ip, itoa.Itoa(a.Port))
}

func (a *TCPAddr) isWildcard() bool {
	if a == nil || a.IP == nil {
		return true
	}
	return a.IP.IsUnspecified()
}

func (a *TCPAddr) opAddr() Addr {
	if a == nil {
		return nil
	}
	return a
}

// ResolveTCPAddr返回TCP端点的地址。
// 
// 网络必须是TCP网络名称。
// 
// 如果address参数中的主机不是文字IP地址或
// 端口不是文字端口号，ResolvetCPADR将
// 地址解析为TCP端点的地址。
// 否则，它将地址解析为一对文字IP地址
// 和端口号。
// address参数可以使用主机名，但不建议使用此参数，因为它最多会返回主机名的一个
// IP地址。
// 
// 有关网络和地址的说明，请参阅func Dial 
// 参数。
func ResolveTCPAddr(network, address string) (*TCPAddr, error) {
	switch network {
	case "tcp", "tcp4", "tcp6":
	case "": // Go 1.0未记录行为的提示通配符
		network = "tcp"
	default:
		return nil, UnknownNetworkError(network)
	}
	addrs, err := DefaultResolver.internetAddrList(context.Background(), network, address)
	if err != nil {
		return nil, err
	}
	return addrs.forResolve(network, address).(*TCPAddr), nil
}

// TCPConn是TCP网络连接的Conn接口实现
// 。
type TCPConn struct {
	conn
}

// SyscallConn返回原始网络连接。
// 这实现了syscall.Conn接口。
func (c *TCPConn) SyscallConn() (syscall.RawConn, error) {
	if !c.ok() {
		return nil, syscall.EINVAL
	}
	return newRawConn(c.fd)
}

// ReadFrom实现io.ReaderFrom ReadFrom方法。
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	n, err := c.readFrom(r)
	if err != nil && err != io.EOF {
		err = &OpError{Op: "readfrom", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return n, err
}

// CloseRead关闭TCP连接的读取端。
// 大多数呼叫者应该只使用Close。
func (c *TCPConn) CloseRead() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	if err := c.fd.closeRead(); err != nil {
		return &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return nil
}

// CloseWrite关闭TCP连接的写入端。
// 大多数呼叫者应该只使用Close。
func (c *TCPConn) CloseWrite() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	if err := c.fd.closeWrite(); err != nil {
		return &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return nil
}

// SetLinger设置在仍有数据等待发送或确认的连接上关闭的行为。
// 
// 如果sec<0（默认值），则操作系统在后台完成发送
// 数据。
// 
// 如果sec==0，操作系统将丢弃任何未发送或
// 未确认的数据。
// 
// 如果sec>0，数据在后台发送，sec<0。在
// 某些操作系统上，秒过后，剩余的
// 未发送的数据可能会被丢弃。
func (c *TCPConn) SetLinger(sec int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	if err := setLinger(c.fd, sec); err != nil {
		return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return nil
}

// SetKeepAlive设置操作系统是否应在连接上发送
// 保持活动消息。
func (c *TCPConn) SetKeepAlive(keepalive bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	if err := setKeepAlive(c.fd, keepalive); err != nil {
		return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return nil
}

// SetKeepAlivePeriod设置两次保存之间的时间间隔。
func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	if err := setKeepAlivePeriod(c.fd, d); err != nil {
		return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return nil
}

// SetNodeDelay控制操作系统是否应该延迟
// 数据包传输以希望发送更少的数据包（Nagle的
// 算法）。默认值为true（无延迟），表示数据在写入后尽快发送。
func (c *TCPConn) SetNoDelay(noDelay bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	if err := setNoDelay(c.fd, noDelay); err != nil {
		return &OpError{Op: "set", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return nil
}

func newTCPConn(fd *netFD) *TCPConn {
	c := &TCPConn{conn{fd}}
	setNoDelay(c.fd, true)
	return c
}

// DialTCP的作用类似于TCP网络的拨号。
// 
// 网络必须是TCP网络名；有关详细信息，请参阅func拨号。
// 
// 如果laddr为零，则自动选择本地地址。
// 如果raddr的IP字段为零或未指定的IP地址，则假定为
// 本地系统。
func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) {
	switch network {
	case "tcp", "tcp4", "tcp6":
	default:
		return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(network)}
	}
	if raddr == nil {
		return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
	}
	sd := &sysDialer{network: network, address: raddr.String()}
	c, err := sd.dialTCP(context.Background(), laddr, raddr)
	if err != nil {
		return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
	}
	return c, nil
}

// TCPListener是一个TCP网络侦听器。客户端通常应该使用Listener类型的变量，而不是假设TCP。
type TCPListener struct {
	fd *netFD
	lc ListenConfig
}

// SyscallConn返回原始网络连接。
// 这实现了syscall.Conn接口。
// 
// 返回的RawConn只支持调用控件。读和写返回一个错误。
func (l *TCPListener) SyscallConn() (syscall.RawConn, error) {
	if !l.ok() {
		return nil, syscall.EINVAL
	}
	return newRawListener(l.fd)
}

// 接受TCP接受下一个传入呼叫并返回新的
// 连接。
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
	if !l.ok() {
		return nil, syscall.EINVAL
	}
	c, err := l.accept()
	if err != nil {
		return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
	}
	return c, nil
}

// Accept在监听器接口实现Accept方法；它
// 等待下一个调用并返回通用连接。
func (l *TCPListener) Accept() (Conn, error) {
	if !l.ok() {
		return nil, syscall.EINVAL
	}
	c, err := l.accept()
	if err != nil {
		return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
	}
	return c, nil
}

// Close停止侦听TCP地址。
// 未关闭已接受的连接。
func (l *TCPListener) Close() error {
	if !l.ok() {
		return syscall.EINVAL
	}
	if err := l.close(); err != nil {
		return &OpError{Op: "close", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
	}
	return nil
}

// Addr返回侦听器的网络地址a*TCPAddr。
// 返回的Addr由Addr的所有调用共享，因此
// 不要修改它。
func (l *TCPListener) Addr() Addr { return l.fd.laddr }

// 设置与侦听器关联的截止日期。
// 时间值为零将禁用截止日期。
func (l *TCPListener) SetDeadline(t time.Time) error {
	if !l.ok() {
		return syscall.EINVAL
	}
	if err := l.fd.pfd.SetDeadline(t); err != nil {
		return &OpError{Op: "set", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
	}
	return nil
}

// 文件返回底层os.File的副本。
// 完成后，调用方有责任关闭f。
// 结束l不影响f，结束f不影响l。
// 
// 返回的os.File的文件描述符与
// 连接的文件描述符不同。试图更改原始
// 的属性使用此副本可能会或可能不会产生预期效果。
func (l *TCPListener) File() (f *os.File, err error) {
	if !l.ok() {
		return nil, syscall.EINVAL
	}
	f, err = l.file()
	if err != nil {
		return nil, &OpError{Op: "file", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
	}
	return
}

// ListenTCP的作用类似于TCP网络的侦听。
// 
// 网络必须是TCP网络名称；有关详细信息，请参阅func拨号。
// 
// 如果laddr的IP字段为零或未指定的IP地址，
// ListenTCP将侦听本地系统的所有可用单播和选播IP地址
// 。
// 如果laddr的端口字段为0，则自动选择端口号
// 。
func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error) {
	switch network {
	case "tcp", "tcp4", "tcp6":
	default:
		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(network)}
	}
	if laddr == nil {
		laddr = &TCPAddr{}
	}
	sl := &sysListener{network: network, address: laddr.String()}
	ln, err := sl.listenTCP(context.Background(), laddr)
	if err != nil {
		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
	}
	return ln, nil
}

// roundDurationUp将d四舍五入到to的下一个倍数。
func roundDurationUp(d time.Duration, to time.Duration) time.Duration {
	return (d + to - 1) / to
}
